package com.ldu.exam.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.transaction.Transactional;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.ldu.exam.assistant.BeanAssistant;
import com.ldu.exam.dao.AccountRepository;
import com.ldu.exam.dao.ClaRepository;
import com.ldu.exam.dao.ExamRepository;
import com.ldu.exam.dao.NoticeRepository;
import com.ldu.exam.dao.RoleRepository;
import com.ldu.exam.model.AccountModel;
import com.ldu.exam.model.AccountModelForm;
import com.ldu.exam.model.ClaModel;
import com.ldu.exam.model.ExamModel;
import com.ldu.exam.model.NoticeModel;
import com.ldu.exam.model.RoleModel;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Service
public class AccountService 
{
	private final String LDAP_ATTRIBUTE_ACCOUNT = "sAMAccountName" ;
	
	@Autowired private AccountRepository  accountRepository ;
	@Autowired private RoleRepository  roleRepository ;
	@Autowired private ExamRepository examRepository ;
	@Autowired private NoticeRepository noticeRepository ;
	@Autowired private ClaRepository claRepository;
	
	/**
	 * 查询全部账户信息
	 * @return
	 */
	@Value("${c.role.admin}") private String[] C_ROLE_ADMIN ;
	public Page<AccountModel> findAll( Pageable page)
	{
		return this.accountRepository.findByNameNotIn( C_ROLE_ADMIN, page) ;
	}
	
	/**
	 * 查询账户信息
	 * @param id
	 * @return
	 */
	public AccountModel find( String id)
	{
		return this.accountRepository.findById(id).orElse(null) ;
	}
	
	public AccountModel findByName( String name)
	{
		return this.accountRepository.findByName( name) ;
	}
	
	public List<AccountModel> findAllNoPage()
	{
		return this.accountRepository.findAll();
	}
	
	/**
	 * 删除账户信息
	 * @param id
	 */
	public void delete( String id )
	{
		this.accountRepository.deleteById(id);
	}
	
	/**
	 * 新增账户信息
	 * @param model
	 * @return
	 */
	public AccountModel save (AccountModel model,String roleId)
	{
		
		AccountModel accountModel = this.accountRepository.save(model);
		RoleModel rmForSave = this.roleRepository.findById(roleId).orElse(null);
		rmForSave.getAccounts().add(accountModel);
		this.roleRepository.save(rmForSave);
	
//		ClaModel cmm = null;
//		if(roleModel.getName().equals("教师"))
//		{
//			if(StringUtils.isEmpty(claId))
//			{
//				return null;
//			}
//			List<String> list = this.accountRepository.isClassIdExists(claId);
//			if(list.size()>0)
//			{
//				return null;
//			}
//			cmm = this.claRepository.findById(claId).orElse(null);
//			if(cmm != null)
//			{
//				model.getCla().add(cmm);
//			}
////			model.setManagerClassId(model.getCla().getId);
//		}
//		model.getRoles().add(roleModel);
//		AccountModel accountModel = this.accountRepository.save(model);
//		roleModel.getAccounts().add(accountModel);
//		this.roleRepository.save(roleModel);
//		if(roleModel.getName().equals("教师"))
//		{
//			cmm.setAccount(accountModel);
//			this.claRepository.save(cmm);
//		}
		return accountModel;
	}

	
//	/**
//	 * 根据登陆账户校验操作权限
//	 * @param username
//	 * @param method
//	 * @param uri
//	 * @return
//	 */
//	public boolean authorize( String username, String method, String uri) 
//	{
//		AccountModel account = this.accountRepository.findByName( username) ;
//		
//		if( null == account)
//			throw new FooException( "用户不存在") ;
//		
//		Set<RoleModel> roles = account.getRoles() ;
//		
//		HashSet<ResourceModel> set = new HashSet<>();
//		
//		roles.forEach( x->{
//				set.addAll( x.getResources()) ;
//		});
//		
//		PathMatcher matcher = new AntPathMatcher() ;
//		
//		return set.stream().anyMatch( x-> method.equalsIgnoreCase( x.getMethod()) && matcher.match( x.getUrl(), uri)) ;
//	}

	
	@Autowired private LdapTemplate ldapTemplate;
	@Value("${c.token.secret}") private String TOKEN_SECRET_KEY ;
	public ResponseEntity<?> authenticate(String username, String password) 
	{
		try 
		{
			//TODO : remove test account
//			if( ! "root".equals( username))
//			{
//				this.ldapTemplate.authenticate ( 
//						LdapQueryBuilder.query().where( LDAP_ATTRIBUTE_ACCOUNT).is( username) , 
//						new String ( Base64.getDecoder().decode( password))
//				);
//			}
			AccountModel account = null;
			if(! "root".equals( username))
			{
				account = this.accountRepository.findByNameAndPassword( username,password);
			}else {
				account = this.accountRepository.findByName(username);
			}
			
			List<NoticeModel> list = this.noticeRepository.findByAccountIdAndIsRead(account.getId(), "0");
			HashMap<String,Object> claims = new HashMap<String,Object>() ;
			claims.put( "userid", account.getId()) ;
			
			String token = Jwts .builder()
								.signWith( SignatureAlgorithm.HS256, TOKEN_SECRET_KEY)
								.setClaims(claims)

								.setIssuedAt( new Date())
								.setAudience( username)
								.setExpiration( null)
								.compact()
			;
			
			HashMap<String,Object> map = new HashMap<String,Object>() ;
			map.put( "token" , token);
//			map.put( "userNickName", account.getNickname());
//			map.put( "id", account.getId());
//			map.put( "pic", account.getPic());
//			if(account.getRoles()!=null)
//			{
//				map.put("role", account.getRoles());
//			}
//			if(account.getCla()!=null)
//			{
//				if(account.getCla().getExam()!=null)
//				{
//					map.put( "examTime", account.getCla().getExam().getExamTime());
//				}
//			}
//			if(!StringUtils.isEmpty(account.getIsExamed()))
//			{
//				map.put("isExamed", account.getIsExamed());
//			}else {
//				map.put("isExamed", "0");
//			}
			map.put("user", account);
			map.put("noticeNum", list.size());
			return  ResponseEntity.ok( map) ; 
			
		} catch (Exception e) {
			e.printStackTrace();
			return ResponseEntity.status( HttpStatus.UNAUTHORIZED).build();
		}
	}

	public Page<AccountModel> findByNameContaining( String name, Pageable page) 
	{
		return this.accountRepository.findByNameContaining( name,page);
	}

	public Object patch( String id , AccountModel model)
	{
//		AccountModel model = this.across(model1);
		
		model.setId( null);
		
		AccountModel origin = this.find( id) ;
		
		String [] nullPropertyNames = BeanAssistant.getNullPropertyNames( model) ;
		
		BeanUtils.copyProperties( model, origin, nullPropertyNames);
		
		return this.accountRepository.save( origin);
		
	}

	public Object updateStudentCla (String id , String claId)
	{
		AccountModel am = this.accountRepository.findById(id).orElse(null);
		ClaModel cm = this.claRepository.findById(claId).orElse(null);
		if(am != null && cm != null)
		{
			cm.getStudentAccounts().add(am);
			this.claRepository.save(cm);
			am.setStudentCla(cm);
			return this.accountRepository.save(am);
		}
		return null;
	}
	
	@Transactional
	public Object putCla(String id, Set<ClaModel> cla)
	{
		this.accountRepository.removeCla(id);
		AccountModel am = this.accountRepository.findById(id).orElse(null);
		Set<ClaModel> clas = new HashSet<ClaModel>();
		for (ClaModel claModel : cla) {
			ClaModel cm = this.claRepository.findById(claModel.getId()).orElse(null);
			if(cm != null)
			{
				cm.setAccount(am);
			}
			clas.add(cm);
		}
		return this.claRepository.saveAll(clas);
	}
	
	@Transactional
	public Object putRoles( String id, Set<RoleModel> roles) 
	{
		this.accountRepository.removeRelRoles( id);
		
		Set<RoleModel> set = roles.stream().map(x->{
			RoleModel role = this.roleRepository.findById( x.getId()).orElse(null) ;
			
			if( null != role)
				role.getAccounts().add( AccountModel.builder().id(id).build()) ;

			return role ;
		}) .collect( Collectors.toSet());
		
		return this.roleRepository.saveAll( set);
	}

	@Transactional
	public Object putExams( List<String> userIds, ExamModel model)
	{
		List<AccountModel> list = new ArrayList<AccountModel>();
		for (String id : userIds) {
			this.accountRepository.removeRelExams(id);
			AccountModel am = this.accountRepository.findById(id).orElse(null);
			ExamModel em = this.examRepository.findById(model.getId()).orElse(null);
			Set<ExamModel> set = new HashSet<ExamModel>();
			set.add(model);
			this.examRepository.save(em);
			list.add(this.accountRepository.save(am));
		}
		return list;
	}
	
	public Object findStudents(int page, int size) 
	{
		List<RoleModel> list = this.roleRepository.findByName("学生");
		if(list.size() > 0)
		{
			return this.accountRepository.findStudents(list.get(0).getId(), PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object findTeachers(int page, int size)
	{
		List<RoleModel> list = this.roleRepository.findByName("教师");
		if(list.size() > 0)
		{
			return this.accountRepository.findStudents(list.get(0).getId(), PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object findStudentsByClassId(String classId,int page, int size)
	{
		List<RoleModel> list = this.roleRepository.findByName("学生");
		if(list.size() > 0)
		{
			return this.accountRepository.findStudentsByClassId(list.get(0).getId(), classId, PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object findTeachersByClassId(String classId,int page, int size)
	{
		List<RoleModel> list = this.roleRepository.findByName("教师");
		if(list.size() > 0)
		{
			return this.accountRepository.findStudentsByClassId(list.get(0).getId(), classId, PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object findTeachersByNickname(String nickname,int page ,int size)
	{
		List<RoleModel> list = this.roleRepository.findByName("教师");
		if(list.size()>0)
		{
			return this.accountRepository.findStudentsByNickname(list.get(0).getId(), nickname, PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object findStudentsByNickname(String nickname,int page ,int size)
	{
		List<RoleModel> list = this.roleRepository.findByName("学生");
		if(list.size()>0)
		{
			return this.accountRepository.findStudentsByNickname(list.get(0).getId(), nickname, PageRequest.of(page, size));
		}else {
			return null;
		}
	}
	
	public Object updatePwd11(String id,String oldPass, String newPass)
	{
		AccountModel model = this.find(id);
		if(model!=null) 
		{
			if(!model.getPassword().equals(oldPass))
			{
				Map map = new HashMap();
				map.put("success", false);
				map.put("message", "更改失败,原密码错误!");
				return map;
			}else {
				model.setPassword(newPass);
				return this.accountRepository.save(model);
			}
			
		}
		return null;
	}
	
	public Object hadNickname(String id)
	{
		AccountModel model = this.find(id);
		if(!StringUtils.isEmpty(model))
		{
			if(StringUtils.isEmpty(model.getNickname()))
			{
				return true;
			}else {
				return false;
			}
		}
		return null;
	}
	
	private AccountModel across(AccountModelForm model1)
	{
		AccountModel model = new AccountModel();
		if(!StringUtils.isEmpty(model1.getId()))
		model.setId(model1.getId());
		if(!StringUtils.isEmpty(model1.getName()))
		model.setName(model1.getName());
		if(!StringUtils.isEmpty(model1.getPassword()))
		model.setPassword(model1.getPassword());
		if(!StringUtils.isEmpty(model1.getSuspended()))
		model.setSuspended(model1.getSuspended());
		if(!StringUtils.isEmpty(model1.getNickname()))
		model.setNickname(model1.getNickname());
		if(!StringUtils.isEmpty(model1.getAddress()))
		model.setAddress(model1.getAddress());
		if(!StringUtils.isEmpty(model1.getGender()))
		model.setGender(model1.getGender());
		if(!StringUtils.isEmpty(model1.getAge()))
			model.setAge(model1.getAge());
		if(!StringUtils.isEmpty(model1.getSummary()))
			model.setSummary(model1.getSummary());
		if(!StringUtils.isEmpty(model1.getState()))
			model.setState(model1.getState());
		if(!StringUtils.isEmpty(model1.getEmail()))
			model.setEmail(model1.getEmail());
		if(!StringUtils.isEmpty(model1.getBirthDate()))
			model.setBirthDate(model1.getBirthDate());
		if(!StringUtils.isEmpty(model1.getPic()))
			model.setPic(model1.getPic());
		if(!StringUtils.isEmpty(model1.getPhone()))
			model.setPhone(model1.getPhone());
		return model;
	}
}
